﻿using System;
using System.Collections;
using System.Text;
using System.Data;
using System.Drawing;
using ZedGraph;
using System.Windows.Forms;

namespace SpeakerRecognition
{
    /// <summary>
    /// Class for operating all MFCC process from the GUI
    /// </summary>
    public class LogicLayerManager
    {
        //************************  Private Fields  *******************************// 
        private FeatureExtractorData[] saveData;
        private String[] inputFiles;
        private String[] inputFilesNamesOnly;
        private double alpha;
        private int winSize;
        private bool usingHamming;
        private bool useStripSilence;
        private bool realOnly;
        private bool useFirstCoeffAC;
        private bool getHighFreq;
        private int overLap;
        private bool powMagnitude;
        private int numberOfMFCC;
        private bool usingMelFilter;
        private int numberOfClusters;
        private int numberOfSteps = 6;
        private String windowType;     // Hamming window
        private DistanceMeasurement distanceType;
        private CommandInvoker invoker;
        private WavFileExtractorCommand wavReadCmd ;
        private PreEmphasisCommand preEmphasisCommand ;
        private CreateFramesCommand createFramesCommand ;
        private FFTCommand fftcommand;
        private LogEnergyMagnitudeCommand logMagCommand;
        private IFFTCommand ifftCommand;
        private PrepareDataAndRunPAMCommand prepareDataAndRunPAMCommand;
        private ArrayList dataToPAM;
        private ArrayList inputMFCCDataToPAM;
        private double[,] distanceMatrix;
        private ArrayList results;
        private ExpectedResults expectedResults;
        private SilenceRedeuceCommand silenceRedeuceCommand;
        private Color[] colorArray = { Color.Red, Color.Blue, Color.Green, Color.Yellow, Color.Pink, Color.Purple, Color.Coral, Color.Blue, Color.DarkGreen, Color.DarkRed, Color.Fuchsia, Color.IndianRed, Color.Violet, Color.Turquoise, Color.SteelBlue, Color.Silver, Color.Salmon, Color.SandyBrown, Color.SeaGreen, Color.PapayaWhip, Color.Olive, Color.Orange, Color.Orchid, Color.MediumSpringGreen };

        //************************  Constructors    *******************************// 
        public LogicLayerManager()
        {
            invoker = new CommandInvoker();
            wavReadCmd = new WavFileExtractorCommand();
            preEmphasisCommand = new PreEmphasisCommand();
            createFramesCommand = new CreateFramesCommand();
            fftcommand = new FFTCommand();
            logMagCommand = new LogEnergyMagnitudeCommand();
            ifftCommand = new IFFTCommand();
            prepareDataAndRunPAMCommand = new PrepareDataAndRunPAMCommand();
            dataToPAM = new ArrayList();
            results = new ArrayList();
            expectedResults = new ExpectedResults();
            silenceRedeuceCommand = new SilenceRedeuceCommand();

        }
        //************************  Public Properties  ****************************// 

        public double[,] DistanceMatrix
        {
            get { return distanceMatrix; }
            set { distanceMatrix = value; }
        }

        public ArrayList InputMFCCDataToPAM
        {
            get { return inputMFCCDataToPAM; }
            set { inputMFCCDataToPAM = value; }
        }
        
        public ArrayList Results
        {
            get { return results; }
            set { results = value; }
        }

        public String[] InputFilesNamesOnly
        {
            get { return inputFilesNamesOnly; }
            set { inputFilesNamesOnly = value; }
        }

        public int WinSize
        {
            get { return winSize; }
            set { winSize = value; }
        }

        public String WindowType
        {
            get { return windowType; }
            set { windowType = value; }
        }

        public int OverLap
        {
            get { return overLap; }
            set { overLap = value; }
        }

        public bool UsingHamming
        {
            get { return usingHamming; }
            set { usingHamming = value; }
        }

        public bool UseStripSilence
        {
            get { return useStripSilence; }
            set { useStripSilence = value; }
        }

        public double Alpha
        {
            get { return alpha; }
            set { alpha = value; }
        }

        public String[] InputFiles
        {
            get { return inputFiles; }
            set { inputFiles = value; }
        }

        public FeatureExtractorData[] SaveData
        {
            get { return saveData; }
            set { saveData = value; }
        }

        public bool RealOnly
        {
            get { return realOnly; }
            set { realOnly = value; }
        }

        public bool PowMagnitude
        {
            get { return powMagnitude; }
            set { powMagnitude = value; }
        }

        public int NumberOfMFCC
        {
            get { return numberOfMFCC; }
            set { numberOfMFCC = value; }
        }

        public bool GetHighFreq
        {
            get { return getHighFreq; }
            set { getHighFreq = value; }
        }

        public bool UseFirstCoeffAC
        {
            get { return useFirstCoeffAC; }
            set { useFirstCoeffAC = value; }
        }

        public int NumberOfClusters
        {
            get { return numberOfClusters; }
            set { numberOfClusters = value; }
        }

        public DistanceMeasurement DistanceType
        {
            get { return distanceType; }
            set { distanceType = value; }
        }

        public bool UsingMelFilter
        {
            get { return usingMelFilter; }
            set { usingMelFilter = value; }
        }

        public int NumberOfSteps
        {
            get { return numberOfSteps; }
            set { numberOfSteps = value; }
        }

        public ArrayList DataToPAM
        {
            get { return dataToPAM; }
            set { dataToPAM = value; }
        }

        //************************  Public Methods  ****************************// 
        /// <summary>
        /// 
        /// </summary>
        public void clearLogicManagerResults()
        {
            invoker = new CommandInvoker();
            wavReadCmd = new WavFileExtractorCommand();
            preEmphasisCommand = new PreEmphasisCommand();
            createFramesCommand = new CreateFramesCommand();
            fftcommand = new FFTCommand();
            logMagCommand = new LogEnergyMagnitudeCommand();
            ifftCommand = new IFFTCommand();
            prepareDataAndRunPAMCommand = new PrepareDataAndRunPAMCommand();
            dataToPAM = new ArrayList();
            results = new ArrayList();
        }
      
        /// <summary>
        /// 
        /// </summary>
        /// <param name="array"></param>
        /// <returns></returns>
        public double findMaxValueInArray(double[] array)
        {
            double max = array[0];
            for (int i = 1; i < array.Length; i++)
            {
                if (array[i] > max)
                    max = array[i];
            }
            return max;
        }

        /// <summary>
        /// This method will run the full process 
        /// </summary>
        /// <param name="progressBar_Main">Progress bar GUI element</param>
        /// <param name="labelProg">Label for process steps</param>
        /// <returns></returns>
        public ArrayList runCompleteProcess(ProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
                // create a feature data saver for each input WAV file 
            saveData = new FeatureExtractorData[inputFiles.Length];
                // calculate the maximum value of the progress bar
            progressBar_Main.Maximum = inputFiles.Length * numberOfSteps * progressBar_Main.Step;

            // create command invoker for invoking the commands


                // loop to extract features from each input file
            for (int i = 0; i < inputFiles.Length; i++)
            {
                     // create a new save object
                saveData[i] = new FeatureExtractorData(inputFiles[i]);
                wavFileExtractorPerformed(i,progressBar_Main,labelProg);
                if (useStripSilence)
                {
                    silenceRedeucePreform(i, labelProg);
                }
                preEmphasisPerform( i, progressBar_Main, labelProg );
                createFramesPerformed(i,progressBar_Main,labelProg);
                fftPerformed(i,progressBar_Main,labelProg);
                logEnergyMagnitudePerformed(i,progressBar_Main,labelProg);
                iffPerformed(i,progressBar_Main,labelProg);
                DataToPAM.Add(saveData[i].IfftData);
            }
                // cluster the results
            clusteringPerformed(progressBar_Main, labelProg);
            return Results;
          }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Filename"></param>
        /// <returns></returns>
        public double[] getWavData(String Filename)
        {
            WavFileExtractorCommand cmd = new WavFileExtractorCommand();
            cmd.FilePath = Filename;
            cmd.UsingStereo = true;
            cmd.execute();
            return cmd.OutputData;
        }

        /// <summary>
        /// this method will convert the results to data table
        /// </summary>
        /// <returns>Data table which contains the clustering results</returns>
        public DataTable createResultsDataTable()
        {
            DataTable inventory = new DataTable("ClusterList");

            // Create DataColumn objects.
            DataColumn[] clusters = new DataColumn[results.Count];
            int numberOfRows = findMaxNumberOfRows();
            DataRow[] newRow = new DataRow[numberOfRows];

            for (int i = 0; i < numberOfRows; i++)
            {
                newRow[i] = inventory.NewRow();
            }
            for (int i = 0; i < results.Count; i++)
            {
                clusters[i] = new DataColumn("Cluster " + (i + 1));

                // Add columns to data table.
                inventory.Columns.Add(clusters[i]);
                int[] cluster = (int[])results[i];
                for (int j = 0; j < cluster.Length; j++)
                {
                    newRow[j][clusters[i]] = inputFilesNamesOnly[cluster[j]];
                }
            }
            for (int i = 0; i < numberOfRows; i++)
            {
                inventory.Rows.Add(newRow[i]);
            }
            return inventory;
        }

        /// <summary>
        /// this method will convert the distance to data table
        /// </summary>
        /// <returns>Data table which contains the clustering results</returns>
        public DataTable createDistanceDataTable()
        {
            DataTable inventory = new DataTable("Distances");
            int matSize = DistanceMatrix.GetLength(0);

            // Create DataColumn objects.
            DataColumn[] clusters = new DataColumn[matSize];
            for (int i = 0; i < matSize; i++)
            {
                String temp = InputFilesNamesOnly[i].Substring(0, InputFilesNamesOnly[i].LastIndexOf('.'));
                inventory.Columns.Add(new DataColumn(temp));
            }

            DataRow[] newRow = new DataRow[matSize];
            for (int i = 0; i < matSize; i++)
            {
                newRow[i] = inventory.NewRow();
                for (int j = 0; j < matSize; j++)
                {
                    if (distanceMatrix[i, j] != 0)
                        newRow[i][j] = String.Format("{0:0.00}", distanceMatrix[i, j]);
                    else
                        newRow[i][j] = 0;
                }
                inventory.Rows.Add(newRow[i]);
            }
            return inventory;
        }

        /// <summary>
        /// Build the Chart
        /// </summary>
        /// <param name="zg1"></param>
        public void CreateGraph(ZedGraphControl zg1)
        {
            // create expected results for graph building
            expectedResults.InputFileNames = InputFilesNamesOnly;
            expectedResults.NumberOfClusters = NumberOfClusters;
            expectedResults.claculateExpectedResults(Results);

            zg1.GraphPane.CurveList.Clear();
            zg1.GraphPane.GraphObjList.Clear();

            // get a reference to the GraphPane
            GraphPane myPane = zg1.GraphPane;

            myPane.BarSettings.Type = BarType.Stack;

            // Set the Titles
            myPane.Title.Text = "Clustering Result";
            myPane.XAxis.Title.Text = "File name groups";
            myPane.YAxis.Title.Text = "Number of files in cluster";


            string[] labels = expectedResults.XValues;

            BarItem[] myBarItems = new BarItem[NumberOfClusters];

            for (int i = 0; i < NumberOfClusters ; i++)
            {
                // Generate a red bar with "Curve 1" in the legend
                myBarItems[i] = myPane.AddBar("Cluster "+ (i + 1), null, (double[])expectedResults.YValues[i],
                                                            colorArray[i]);
                myBarItems[i].Bar.Fill = new Fill(colorArray[i], Color.White, colorArray[i]);
            }


            // Draw the X tics between the labels instead of 
            // at the labels
            myPane.XAxis.MajorTic.IsBetweenLabels = true;

            // Set the XAxis labels
            myPane.XAxis.Scale.TextLabels = labels;
            // Set the XAxis to Text type
            myPane.XAxis.Type = AxisType.Text;

            // Fill the Axis and Pane backgrounds
            myPane.Chart.Fill = new Fill(Color.White,
                  Color.FromArgb(255, 255, 166), 90F);
            myPane.Fill = new Fill(Color.FromArgb(250, 250, 255));

            // Tell ZedGraph to refigure the
            // axes since the data have changed
            zg1.AxisChange();
        }
        //************************  Private Methods  ****************************// 
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private int findMaxNumberOfRows()
        {
            int max = 0;
            for (int i = 0; i < results.Count; i++)
            {
                int[] cluster = (int[])results[i];
                if (cluster.Length > max)
                {
                    max = cluster.Length;
                }
            }
            return max;
        }

        /// <summary>
        /// perform a step in the GUI progress bar
        /// </summary>
        private void performOneStep(ProgressBar progressBar_Main)
        {
            progressBar_Main.PerformStep();
            progressBar_Main.Update();
        }

        /// <summary>
        /// perform a step in the GUI label
        /// </summary>
        /// <param name="labelProg"></param>
        /// <param name="text"></param>
        private void updateProgressLabel(System.Windows.Forms.Label labelProg, String text)
        {
            labelProg.Text = text;
            labelProg.Update();
        }

        /// <summary>
        ///  
        /// </summary>
        /// <param name="i"></param>
        /// <param name="labelProg"></param>
        private void silenceRedeucePreform(int i, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing silence reduction...");
            // execute preEmphasis on the input data

            silenceRedeuceCommand.InputData = saveData[i].WavData;
            silenceRedeuceCommand.FrameSize = WinSize;
            invoker.Invoke(silenceRedeuceCommand);
            saveData[i].WavData = silenceRedeuceCommand.OutputData;
          
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void preEmphasisPerform(int i, ProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing PreEmphasis...");
            // execute preEmphasis on the input data
            preEmphasisCommand.Alpha = alpha;
            preEmphasisCommand.InputData = saveData[i].WavData;
            invoker.Invoke(preEmphasisCommand);
            saveData[i].PreEmpData = preEmphasisCommand.OutputData;
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void wavFileExtractorPerformed(int i, ProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": reading WAV data...");

            // execute read WAV file command to get the double[]
            wavReadCmd.FilePath = saveData[i].WavFileName;
            wavReadCmd.UsingStereo = true;
            invoker.Invoke(wavReadCmd);
            saveData[i].WavData = wavReadCmd.OutPutWithOutHeader;
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void createFramesPerformed(int i, ProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing Windowing...");
                 // execute Windowing and framing on the input data
            createFramesCommand.InputData = saveData[i].PreEmpData;
            createFramesCommand.UsingHammingWindow = usingHamming;
            createFramesCommand.WindowSize = winSize;
            createFramesCommand.OverlapSize = overLap;
            createFramesCommand.WindowType = windowType;
            invoker.Invoke(createFramesCommand);
            saveData[i].FramingData = createFramesCommand.OutputData;
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void  fftPerformed(int i, ProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing FFT on frames...");
                 // execute FFT on frames of the input data
            fftcommand.RealOnly = realOnly;
            double[][] twoDimArr = saveData[i].FramingData;
            saveData[i].FftData = new double[twoDimArr.Length][];
            for (int j = 0; j < twoDimArr.Length; j++)
            {
                fftcommand.InputData = twoDimArr[j];
                invoker.Invoke(fftcommand);
                saveData[i].FftData[j] = fftcommand.MagnitudeData;
            }
            saveData[i].FftDataOneDim = convert2DimArrayTo1Dim(saveData[i].FftData);
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void logEnergyMagnitudePerformed(int i, ProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing Log energy...");
                 // execute Log Energy and framing on the input data
            double[][] logArr = saveData[i].FftData;
            saveData[i].LogEnergyData = new double[logArr.Length][];
            logMagCommand.WithPowerOfTwo = powMagnitude;
            for (int j = 0; j < logArr.Length; j++)
            {
                logMagCommand.InputData = logArr[j];
                invoker.Invoke(logMagCommand);
                saveData[i].LogEnergyData[j] = logMagCommand.OutPutData;
            }
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void iffPerformed(int i, ProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, inputFilesNamesOnly[i] + ": executing IFFT on frames...");
                // execute IFFT on frames of the input data
            double[][] ifftArr = saveData[i].LogEnergyData;
            saveData[i].IfftData = new double[ifftArr.Length][];
            for (int j = 0; j < ifftArr.Length; j++)
            {
                ifftCommand.InputData = ifftArr[j];
                invoker.Invoke(ifftCommand);
                saveData[i].IfftData[j] = ifftCommand.MagnitudeData;
            }
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="progressBar_Main"></param>
        /// <param name="labelProg"></param>
        private void clusteringPerformed(ProgressBar progressBar_Main, System.Windows.Forms.Label labelProg)
        {
            updateProgressLabel(labelProg, "Clustering...");
            prepareDataAndRunPAMCommand.InputData = DataToPAM;
            prepareDataAndRunPAMCommand.NumberOfCluster = numberOfClusters;
            prepareDataAndRunPAMCommand.NumberOfMFCC = numberOfMFCC;
            prepareDataAndRunPAMCommand.WithAC = useFirstCoeffAC;
            prepareDataAndRunPAMCommand.DistanceType = distanceType;
            prepareDataAndRunPAMCommand.GetHighFreq = getHighFreq;
            invoker.Invoke(prepareDataAndRunPAMCommand);
            InputMFCCDataToPAM = prepareDataAndRunPAMCommand.InputToPAM;
            Results = prepareDataAndRunPAMCommand.OutputData;
            DistanceMatrix = prepareDataAndRunPAMCommand.DistMat;
            performOneStep(progressBar_Main);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        private double[] convert2DimArrayTo1Dim(double[][] source)
        {
            int rows = source.Length, cols = source[0].Length;
            ArrayList tempArr = new ArrayList();
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    tempArr.Add(source[i][j]);
                }
            }
            double[] temp = new double[tempArr.Count];

            for (int i = 0; i < tempArr.Count; i++)
            {
                temp[i] = (double)tempArr[i];
            }
            return temp; 
        }
    }


    
}
